/**
 *
 */
package com.cmcc.pcap.packet.sctp;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.pcap4j.packet.SctpPacket;
import org.pcap4j.packet.namednumber.SctpChunkType;
import org.pcap4j.util.ByteArrays;

import java.util.ArrayList;
import java.util.Iterator;


/**
 * (1)SCTP握手消息重传
 * (2)SCTP协议 DATA消息重传 SCTP Retransmission：TSN(relative)和 TSN(absolute)相同
 *
 */
public class SctpConnection {
    public static Logger logger = LogManager.getLogger(SctpConnection.class);

    public String connectionKey;//标识一个Connection的唯一Key，形如  “IP:PORT-IP:PORT”  //在处理Connection的第一个SYN消息时设置
    public String clientAddr;//SCTP连接的客户端，形如 “IP:PORT”//在处理Connection的第一个INIT消息时设置
    public String serverAddr;//SCTP连接的服务端，形如 “IP:PORT”//在处理Connection的第一个INIT消息时设置

    public SctpProtocol protocol = SctpProtocol.SCTP;//SCTP链接上对应的应用层协议，如NGAP等。默认就是SCTP，包括无法识别应用层协议的分片以及无data的包，此处都是SCTP。
    public SctpPacketDecorator firstDataSctpPacketDecorator;//第一个带Tcp Payload/data的包 //在向cache中加入该包且filter过滤后设置
    public ArrayList<SctpPacketDecorator> al;//保存该连接初始收到的所有SctpPacket，TODO ArrayList or ConnectionHashMap?  TODO reassemble后是否需要清空？

    public long lastPacketTime;//connection最后一个packet的加入cache的时间(注:非抓包时间)

    /**
     * 四次握手对应的包
     */
    public SctpPacket handshakeSctpPacket_init;
    public SctpPacket handshakeSctpPacket_init_ack;
    public SctpPacket handshakeSctpPacket_cookie_echo;
    public SctpPacket handshakeSctpPacket_cookie_ack;
    /**
     * 三次挥手对应的包
     */
    public SctpPacket waveSctpPacket_shutdown;
    public SctpPacket waveSctpPacket_shutdown_ack;
    public SctpPacket waveSctpPacket_shutdown_completion;


    /**
     * 此两个list依次依赖前一个list处理，而retransmissionDataMergeA依赖最开始的al
     */
    public ArrayList<SctpPacketDecorator> retransmissionDataMergeAl;//保存Retransmission/Spurious Retransmission merge后的所有SctpPacketDecorator
    public ArrayList<SctpPacketDecorator> duplicateSackMergeAl;//保存去掉重复确认SACK报文后的所有SctpPacketDecorator
    public boolean haveSHUTDOWN = false; //有一条SHUTDOWN packet，就置为true
    /**
     * 部分针对该Connection的统计数据
     */
    public int packetsNumInitial;//初始包数
    public int packetsNumAfterRetransmissionDataMerge;//Retransmission/Spurious Retransmission merge后包数
    public int packetsNumAfterDuplicateSackMerge;//Duplicate ACK merge后的包数
    public boolean retransmissionDataMerge; //是否重传

    public SctpConnection() {
        al = new ArrayList<SctpPacketDecorator>();
    }


    /**
     * 需要依次调用：
     * TODO this.duplicateAckMerge();和this.retransmissionMerge();如果合并，其实可以通过一次遍历完成，可优化性能。
     * TODO 新增ArrayList用完后删除其实可以节省内存
     */
    public void sctpConnectionPacketsDeal() {
        //TODO 去掉sctp协议的重复报文

        this.retransmissionDataMerge();
        this.packetsNumInitial = al.size();
        al.clear();

        this.packetsNumAfterRetransmissionDataMerge = retransmissionDataMergeAl.size();

    }

    /**
     * SCTP报文在不看DATA部分报文的时候是无序的
     * 去掉重复的SCTP报文
     * SCTP Retransmission（SCTP重传）
     *
     */
    public void retransmissionDataMerge() {
        retransmissionDataMergeAl = new ArrayList<SctpPacketDecorator>();

        Iterator<SctpPacketDecorator> intertor = al.iterator();
        while (intertor.hasNext()) {
            SctpPacketDecorator sctpPacketDecorator = intertor.next();

            if(!sctpPacketDecorator.chunktypelist.isEmpty() && sctpPacketDecorator.chunktypelist.contains(SctpChunkType.DATA)){
                int check = this.checkTSN(retransmissionDataMergeAl,sctpPacketDecorator);
                if(check != -1){
                    SctpPacketDecorator spd = retransmissionDataMergeAl.get(check);
                    spd.isRetransmissionDataMerge = true;
                    this.retransmissionDataMerge = true;
                    spd.retransmissionMergeDataAl = new ArrayList<>();
                    spd.retransmissionMergeDataAl.add(sctpPacketDecorator);
                }else{
                    retransmissionDataMergeAl.add(sctpPacketDecorator);
                }
            }else{
                retransmissionDataMergeAl.add(sctpPacketDecorator);
            }
        }
    }

    /**
     * 用于判断SCTP chunktype类型是DATA的报文的TSN是否相同
     * 一个SCTP报文会写到多个chunk数据块，有可能的组合：1.SACK，DATA;2.DATA,DATA
     * 此处只要判断其中只要一个DATA数据块的TSN相同就算重传
     */

    private int checkTSN(ArrayList<SctpPacketDecorator> argAl, SctpPacketDecorator spd){

        for(SctpPacketDecorator saveSpd : argAl){
            if(!saveSpd.chunktypelist.isEmpty() && saveSpd.chunktypelist.contains(SctpChunkType.DATA)){

                ArrayList<Integer> save_chunk_tsn_list = getDataChunkTsnList((ArrayList<SctpPacket.SctpChunk>) saveSpd.sctpPacket.getHeader().getChunks());
                ArrayList<Integer> spd_chunk_tsn_list = getDataChunkTsnList((ArrayList<SctpPacket.SctpChunk>) spd.sctpPacket.getHeader().getChunks());

                for(Integer i : save_chunk_tsn_list) {
                    for (Integer j : spd_chunk_tsn_list) {
                        if (Integer.compare(i,j) == 0) {
                            return argAl.indexOf(saveSpd);
                        }
                    }
                }

            }
        }
        return -1;
    }

    /*
    将一个sctp报文中的chunklist中的datachunk的tsn找到，并存放在List中
     */
    private ArrayList<Integer> getDataChunkTsnList(ArrayList<SctpPacket.SctpChunk> sctpchunklist){
        /*
        tsnList用于顺序存放DATA chunk的tsn
         */
        ArrayList<Integer> tsnList = new ArrayList<>();
        for (SctpPacket.SctpChunk sctpchunk:sctpchunklist){
            if(sctpchunk.getType().name().equals(SctpChunkType.DATA.name())){
                Integer tsn = ByteArrays.getInt(sctpchunk.getRawData(),4,4);
                tsnList.add(tsn);
            }
        }
        return tsnList;

    }


    private int checkTSNACK(ArrayList<SctpPacketDecorator> argAl, SctpPacketDecorator spd){
        for(SctpPacketDecorator saveSpd : argAl){
            if(!saveSpd.chunktypelist.isEmpty() && saveSpd.chunktypelist.contains(SctpChunkType.SACK)){

                ArrayList<Integer> save_chunk_tsn_ack_list = getSackChunkTsnAckList((ArrayList<SctpPacket.SctpChunk>) saveSpd.sctpPacket.getHeader().getChunks());
                ArrayList<Integer> spd_chunk_tsn_ack_list = getSackChunkTsnAckList((ArrayList<SctpPacket.SctpChunk>) spd.sctpPacket.getHeader().getChunks());

                for(Integer i : save_chunk_tsn_ack_list) {
                    for (Integer j : save_chunk_tsn_ack_list) {
                        if (Integer.compare(i,j) == 0) {
                            return argAl.indexOf(saveSpd);
                        }
                    }
                }

            }
        }
        return -1;
    }

    private ArrayList<Integer> getSackChunkTsnAckList(ArrayList<SctpPacket.SctpChunk> sctpchunklist){
        /*
        tsnList用于顺序存放DATA chunk的tsn
         */
        ArrayList<Integer> tsnackList = new ArrayList<>();

        for (SctpPacket.SctpChunk sctpchunk:sctpchunklist){
            if(sctpchunk.getType().name().equals(SctpChunkType.SACK.name())){
                Integer tsn_ack = ByteArrays.getInt(sctpchunk.getRawData(),4,4);
                tsnackList.add(tsn_ack);
            }
        }
        return tsnackList;

    }


}
